package com.x.compute.common.function;

import cn.hutool.core.util.NumberUtil;
import com.googlecode.aviator.runtime.function.AbstractVariadicFunction;
import com.googlecode.aviator.runtime.type.AviatorDecimal;
import com.googlecode.aviator.runtime.type.AviatorJavaType;
import com.googlecode.aviator.runtime.type.AviatorObject;
import com.googlecode.aviator.runtime.type.AviatorType;

import java.math.BigDecimal;
import java.util.*;

/**
 * 自定义SUMIFS函数
 * 函数示例：SUMIFS(arg1,arg2,arg3...)
 * SUMIFS(求和的区域，条件区域1,指定的求和条件1,条件区域2,指定的求和条件2,……)
 *
 * @author xuxin
 * @time 2022/4/13 9:30
 */
public class SumifsFunction extends AbstractVariadicFunction {

    @Override
    public String getName() {
        return "SUMIFS";
    }

    /**
     * 函数处理逻辑
     *
     * @param env  变量上下文
     * @param args 参数集合
     * @return
     */
    @Override
    public AviatorObject variadicCall(Map<String, Object> env, AviatorObject... args) {
        Map<String, Object> sumMap = new HashMap<>((Map<String, Object>) args[0].getValue(env));;
        Set<String> resultSet = conditionJudge(env, args);
        sumMap.keySet().removeIf(key -> !resultSet.contains(key));
        BigDecimal sum = BigDecimal.ZERO;
        for (String key : resultSet) {
            Object value = sumMap.get(key);
            if (value == null || !NumberUtil.isNumber(value.toString())) {
                continue;
            }
            sum = sum.add(new BigDecimal(value.toString()));
        }
        return new AviatorDecimal(sum);
    }

    /**
     * 条件判断
     *
     * @param env
     * @param args
     * @return
     */
    private Set<String> conditionJudge(Map<String, Object> env, AviatorObject... args) {
        Set<String> resultSet = null;
        for (int index = 1; index < args.length; index = index + 2) {
            Map<String, Object> dataSet = new HashMap<>((Map<String, Object>) args[index].getValue(env));
            Object conditionValue = args[index + 1].getValue(env);
            String condition = conditionValue == null && args[index + 1].getAviatorType() == AviatorType.JavaType ? ((AviatorJavaType) args[index + 1]).getName() : Optional
                    .ofNullable(conditionValue).map(Object::toString).orElse("");
            Set<String> valueSet = new HashSet<>();
            for (Map.Entry<String, Object> entry : dataSet.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();
                if (value != null && value.toString().equals(condition)) {
                    valueSet.add(key);
                }
            }
            dataSet.keySet().removeIf(key -> !valueSet.contains(key));
            if (resultSet != null) {
                resultSet.retainAll(valueSet);
                continue;
            }
            resultSet = valueSet;
        }
        return resultSet;
    }
}
